/*
 * SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
 *
 * SPDX-License-Identifier: MulanPSL-2.0
 */
#![allow(
    dead_code,
    mutable_transmutes,
    non_camel_case_types,
    non_snake_case,
    non_upper_case_globals,
    unused_assignments,
    unused_mut,
    clippy::never_loop
)]

use crate::common::*;

extern "C" {
    fn malloc(_: libc::c_ulong) -> *mut libc::c_void;
    fn free(_: *mut libc::c_void);
    fn memcmp(_: *const libc::c_void, _: *const libc::c_void, _: libc::c_ulong) -> libc::c_int;
    fn strlen(_: *const libc::c_char) -> libc::c_ulong;
    fn lseek(__fd: libc::c_int, __offset: __off_t, __whence: libc::c_int) -> __off_t;
    fn sudo_warnx_nodebug_v1(fmt: *const libc::c_char, _: ...);
    static mut sudoers_subsystem_ids: [libc::c_uint; 0];
    fn digest_type_to_name(digest_type: libc::c_int) -> *const libc::c_char;
    fn sudo_debug_printf2_v1(
        func: *const libc::c_char,
        file: *const libc::c_char,
        line: libc::c_int,
        level: libc::c_int,
        fmt: *const libc::c_char,
        _: ...
    );
    fn base64_decode(str: *const libc::c_char, dst: *mut libc::c_uchar, dsize: size_t) -> size_t;
    fn hexchar(s: *const libc::c_char) -> libc::c_int;
    fn sudo_filedigest(
        fd: libc::c_int,
        file: *const libc::c_char,
        digest_type: libc::c_int,
        digest_len: *mut size_t,
    ) -> *mut libc::c_uchar;
}

pub const SEEK_SET: libc::c_int = 0;

#[derive(Copy, Clone)]
#[repr(C)]
pub struct command_digest {
    pub digest_type: libc::c_uint,
    pub digest_str: *mut libc::c_char,
}

#[no_mangle]
pub fn digest_matches(
    mut fd: libc::c_int,
    mut file: *const libc::c_char,
    mut digest: *const command_digest,
) -> bool {
    let mut file_digest: *mut libc::c_uchar = std::ptr::null_mut::<libc::c_uchar>();
    let mut sudoers_digest: *mut libc::c_uchar = std::ptr::null_mut::<libc::c_uchar>();
    let mut matched: bool = false;
    let mut digest_len: size_t = 0;
    unsafe {
        debug_decl!(SUDOERS_DEBUG_MATCH!());
    }

    unsafe {
        'done: loop {
            if fd == -(1 as libc::c_int) {
                break 'done;
            }

            file_digest = sudo_filedigest(
                fd,
                file,
                (*digest).digest_type as libc::c_int,
                &mut digest_len,
            );
            if lseek(fd, 0 as off_t, SEEK_SET) == -(1 as libc::c_int) as libc::c_long {
                sudo_debug_printf!(
                    SUDO_DEBUG_ERROR | SUDO_DEBUG_ERRNO | SUDO_DEBUG_LINENO,
                    b"unable to rewind digest fd\0" as *const u8 as *const libc::c_char
                );
            }
            if file_digest.is_null() {
                /* Warning (if any) printed by sudo_filedigest() */
                break 'done;
            }

            /* Convert the command digest from ascii to binary. */
            sudoers_digest = malloc(digest_len) as *mut libc::c_uchar;
            if sudoers_digest.is_null() {
                sudo_warnx!(
                    b"%s: %s\0" as *const u8 as *const libc::c_char,
                    get_function_name!(),
                    b"unable to allocate memory\0" as *const u8 as *const libc::c_char
                );
                break 'done;
            }
            'bad_format: loop {
                if strlen((*digest).digest_str) == digest_len.wrapping_mul(2 as libc::c_ulong) {
                    /* Convert ascii hex to binary. */
                    let mut i: libc::c_uint = 0;
                    loop {
                        if (i as libc::c_ulong) >= digest_len {
                            break;
                        }
                        let h: libc::c_int =
                            hexchar(&*((*digest).digest_str).offset(i.wrapping_add(i) as isize));
                        if h == -(1 as libc::c_int) {
                            break 'bad_format;
                        }
                        *sudoers_digest.offset(i as isize) = h as libc::c_uchar;
                        i = i.wrapping_add(1);
                    }
                } else {
                    /* Convert base64 to binary. */
                    let mut len: size_t =
                        base64_decode((*digest).digest_str, sudoers_digest, digest_len);
                    if len != digest_len {
                        sudo_debug_printf!(
                            SUDO_DEBUG_ERROR | SUDO_DEBUG_LINENO,
                            b"incorrect length for digest, expected %zu, got %zu\0" as *const u8
                                as *const libc::c_char,
                            digest_len,
                            len
                        );
                        break 'bad_format;
                    }
                }

                if memcmp(
                    file_digest as *const libc::c_void,
                    sudoers_digest as *const libc::c_void,
                    digest_len,
                ) == 0 as libc::c_int
                {
                    matched = true;
                } else {
                    sudo_debug_printf!(
                        SUDO_DEBUG_DIAG | SUDO_DEBUG_LINENO,
                        b"%s digest mismatch for %s, expecting %s\0" as *const u8
                            as *const libc::c_char,
                        digest_type_to_name((*digest).digest_type as libc::c_int),
                        file,
                        (*digest).digest_str
                    );
                }
                break 'done;
            }
            //bad_format:
            sudo_warnx!(
                b"digest for %s (%s) is not in %s form\0" as *const u8 as *const libc::c_char,
                file,
                (*digest).digest_str,
                digest_type_to_name((*digest).digest_type as libc::c_int)
            );
            break;
        }
        //done:
        free(sudoers_digest as *mut libc::c_void);
        free(file_digest as *mut libc::c_void);
        debug_return_bool!(matched);
    } //unsafe
}
